#import <MachOFile.h>
#import <MappedFile.h>
/*
 * $Log:	MachO.m,v $
Revision 1.5  94/05/28  16:17:33  ediger
updated usage() to reflect actual options

Revision 1.4  94/05/26  21:47:00  ediger
"productionized" it

Revision 1.1  94/02/19  14:29:20  ediger
Initial revision
 */

static char MachORcsIdent[] = "$Id: MachO.m,v 1.5 94/05/28 16:17:33 ediger Exp Locker: ediger $";

// deal with user preference in which way "low memory" is on the printed
// page.
enum direction {UP, DOWN};

void print_segments(id mach_o_file, enum direction, BOOL printsects);
void print_threads(id oFile);

void do_cmd_line(int, char **);

// Option flags: global to avoid complicating do_cmd_line()
BOOL gyPrintThreads   = FALSE;
BOOL gyPrintSections  = FALSE;
BOOL gyPrintSegmentsVerbose  = FALSE;
BOOL gyPrintLibraries = FALSE;
enum direction geDir = UP;


void usage(char *);

int
main(int ac, char **av)
{
	MachOFile  *oFile;
	MappedFile *oMap;
	char       *bpFileName = NULL;
#ifdef MALLOCDEBUGGING
	char buf[256];
#endif

	do_cmd_line(ac, av);

#ifdef MALLOCDEBUGGING
	puts("Hit return to continue...");
	gets(buf);
#endif

	bpFileName = av[optind];

	if (bpFileName == NULL)
	{
		fprintf(stderr, "Specify file name on command line.\n");
		usage(*av);
		exit(9);
	}

	oFile = [[MachOFile alloc] init];

	oMap = [[[MappedFile alloc] init] filename:bpFileName];
	if ([oMap map] == FALSE)
	{
		fprintf(stderr, "Trouble mapping \"%s\": %s\n",
			bpFileName, [oMap errorString]);
		[oFile free];
		exit(1);
	}

	if (gyPrintLibraries == TRUE)
		[oFile considerMappedFiles];

	if([oFile fillFromAddress:[oMap address]] == NULL)
	{
		fprintf(stderr, "\"%s\" is probably not a Mach-O file\n",
			bpFileName);
		exit(9);
	}

	if (gyPrintThreads == TRUE)
		print_threads(oFile);

	print_segments(oFile, geDir, gyPrintSections);

	[oFile free];
	[oMap  free];

#ifdef MALLOCDEBUGGING
	puts("Hit return to finish...");
	gets(buf);
#endif

	return 0;
}

void
print_threads(oFile)
	id oFile;
{
	int iThreads, iCC;

	iThreads = [oFile threads];

	for (iCC = 0; iCC < iThreads; ++iCC)
	{	id            oThread = [oFile thread:iCC];
		unsigned long ulAddr = [oThread pc];

		printf("Thread's pc: 0x%lx\n", ulAddr);
	}
}

void
print_segments(oFile, eDirection, ySections)
	id   oFile;
	enum direction eDirection;
	BOOL ySections;
{
	int iSegments, iCC;
	int iFrom, iTo, iIncr;

	iSegments = [oFile mappedSegments];

	if (eDirection == UP)
	{
		iFrom = 0;
		iTo   = iSegments;
		iIncr = 1;
	} else {
		iFrom = iSegments - 1;
		iTo   = 0;
		iIncr = -1;
	}

	for (iCC = iFrom; eDirection == UP ? iCC < iTo : iCC >= iTo ; iCC += iIncr)
	{	id  oSegment = [oFile mappedSegment:iCC];
		int iSections;

		if (eDirection == DOWN)
		{
			printf("Segment 0x%lx => 0x%lx named \"%s\"\n",
				[oSegment getBaseAddress],
				[oSegment getUpperAddress],
				[oSegment commandName]);

			if (gyPrintSegmentsVerbose)
			{
				struct segment_command *spSegCmd =
					(struct segment_command *)[oSegment loadCommandAddress];

				printf("\tSegment at 0x%lx, size 0x%lx, offset in file 0x%lx, size 0x%lx\n",
					spSegCmd->vmaddr,
					spSegCmd->vmsize,
					spSegCmd->fileoff,
					spSegCmd->filesize);
			}
		}

		if (ySections == TRUE && (iSections = [oSegment numberOfSections]) > 0)
		{	int iC;
			int iInFrom, iInTo, iInIncr;

			if (eDirection == UP)
			{
				iInFrom = iSections - 1;
				iInTo   = 0;
				iInIncr = -1;
			} else {
				iInFrom = 0;
				iInTo   = iSections;
				iInIncr = 1;
			}

			for (iC = iInFrom; eDirection == UP ? iC >= iInTo : iC < iInTo;
				iC += iInIncr)
			{	struct section *sect = [oSegment getSection:iC];
				printf("\t%s in seg %s - 0x%lx, %lu bytes\n",
					sect->sectname, sect->segname,
					sect->addr, sect->size);
			}
		}

		if (eDirection == UP)
		{
			printf("Segment 0x%lx => 0x%lx named \"%s\"\n",
				[oSegment getBaseAddress],
				[oSegment getUpperAddress],
				[oSegment commandName]);

			if (gyPrintSegmentsVerbose)
			{
				struct segment_command *spSegCmd =
					(struct segment_command *)[oSegment loadCommandAddress];

				printf("\tSegment at 0x%lx, size 0x%lx, offset in file 0x%lx, size 0x%lx\n",
					spSegCmd->vmaddr,
					spSegCmd->vmsize,
					spSegCmd->fileoff,
					spSegCmd->filesize);
			}
		}
	}

	return;
}

void
usage(char *abpProgName)
{
	fprintf(stderr, "Usage: %s <options> filename\n", abpProgName);
	fprintf(stderr, "-t   print thread info\n");
	fprintf(stderr, "-s   print info for sections of segments\n");
	fprintf(stderr, "-v   print info for sections of segments verbosely\n");
	fprintf(stderr, "-l   print info for segments of shared libraries used\n");
}

extern char *optarg;
extern int   optind;

void
do_cmd_line(int ac, char **av)
{
	int iOption;

	while ((iOption = getopt(ac, av, "vdtsl")) != EOF)
		switch (iOption)
		{
		case 't': gyPrintThreads         = TRUE;  break;
		case 's': gyPrintSections        = TRUE; break;
		case 'l': gyPrintLibraries       = TRUE; break;
		case 'v': gyPrintSegmentsVerbose = TRUE; break;
		case 'd': geDir                  = DOWN; break;
		default:
			usage(*av);
			exit(9);
			break;
		}
}
